// Clause.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Linq;

using Supremacy.Economy;
using Supremacy.Types;

namespace Supremacy.Diplomacy
{
    public interface IClause
    {
        object Data { get; set; }
        int Duration { get; set; }
        ClauseType ClauseType { get; }
        bool IsDataInitialized { get; }
    }

    internal interface IClauseInternal : IClause
    {
        void SetDataInitialized();
    }

    public static class ClauseExtensions
    {
        public static T GetData<T>(this IClause clause)
        {
            if (clause.Data is T)
                return (T)clause.Data;
            return default(T);
        }

        public static bool TryGetData<T>(this IClause clause, out T data)
        {
            if (clause == null)
            {
                data = default(T);
                return false;
            }
            if (clause.Data is T)
            {
                data = (T)clause.Data;
                return true;
            }
            data = default(T);
            return false;
        }

        public static void InitializeData(this IClause clause)
        {
            if (clause.IsDataInitialized)
                return;

            var clauseInternal = clause as IClauseInternal;

            switch (clause.ClauseType)
            {
                case ClauseType.OfferGiveCredits:
                case ClauseType.RequestGiveCredits:
                    clause.Data = 0;
                    break;

                case ClauseType.OfferGiveResources:
                case ClauseType.RequestGiveResources:
                    clause.Data = new ResourceValueCollection();
                    break;

                default:
                    clause.Data = null;
                    break;
            }

            if (clauseInternal != null)
                clauseInternal.SetDataInitialized();
        }
    }

    [Serializable]
    public class Clause : IClauseInternal
    {
        public const int DurationStepSize = 50;
        public const int ImmediateDuration = 1;
        public const int IndefiniteDuration = 255;
        public const int InvalidDuration = 0;
        public const int MaxFiniteDuration = 250;
        public const int NotEnded = -1;
        public const int NotStarted = -1;

        private readonly IsSetFlag _isDataInitialized;
        private int _duration = ImmediateDuration;

        public Clause() {}

        public Clause(ClauseType clauseType)
        {
            if (clauseType == ClauseType.NoClause)
                throw new ArgumentException("cannot be NoClause", "clauseType");
            _isDataInitialized = new IsSetFlag(false);
            this.ClauseType = clauseType;
            this.InitializeData();
        }

        public Clause(ClauseType clauseType, object data) : this(clauseType)
        {
            if (data == null)
                throw new ArgumentNullException("data");
            this.Data = data;
        }

        protected internal void SetDataInitialized()
        {
            this._isDataInitialized.Set();
        }

        #region IClauseInternal Members
        public ClauseType ClauseType { get; set; }

        public object Data { get; set; }

        public int Duration
        {
            get { return this._duration; }
            set
            {
                if (value > MaxFiniteDuration)
                    value = MaxFiniteDuration;
                else if (value < ImmediateDuration)
                    value = ImmediateDuration;
                this._duration = value;
            }
        }

        public bool IsDataInitialized
        {
            get { return this._isDataInitialized; }
        }

        void IClauseInternal.SetDataInitialized()
        {
            this.SetDataInitialized();
        }
        #endregion
    }
}